/*
 * sys-copyself.c
 *
 * Copyright(c) 2007-2019 Jianjun Jiang <8192542@qq.com>
 * Official site: http://xboot.org
 * Mobile phone: +86-18665388956
 * QQ: 8192542
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include <io.h>

#define SIZE_START	(0x00010000)
extern void return_to_fel(void);
extern void sys_uart_putc(char c);
extern void sdio_read_sector(rt_uint32_t addr_sec,rt_uint8_t *buf);
extern void sdio_write_sector(rt_uint32_t addr_sec,rt_uint8_t *buf);

extern void sys_spinand_init(void);
extern void sys_spinand_exit(void);
extern void sys_spinand_read(int addr, void * buf, int count);

/*
enum {
	BOOT_DEVICE_FEL	= 0,
	BOOT_DEVICE_SPI	= 1
};
*/
enum {
	BOOT_DEVICE_FEL	= 0,
	BOOT_DEVICE_SPINOR,
	BOOT_DEVICE_SPINAND,
	BOOT_DEVICE_SDCARD,
};

struct flash_head {
	uint32_t magic[4];
};

static inline void sdelay(int loops){
	__asm__ __volatile__ ("1:\n" "subs %0, %1, #1\n"
		"bne 1b":"=r" (loops):"0"(loops));
}

/*
static int get_boot_device(void){
	uint32_t * t = (void *)0x00000058;
	if(t[0] == 0x1)
		return BOOT_DEVICE_FEL;
	return BOOT_DEVICE_SPI;
}
*/

static int get_boot_device(void){
	uint32_t * t = (void *)0x00000058;
	if(t[0] == 0x3)
		return BOOT_DEVICE_SPINOR;
	else if(t[0] == 0x4)
		return BOOT_DEVICE_SPINAND;
	else if(t[0] == 0x0)
		return BOOT_DEVICE_SDCARD;
	else if(t[0] == 0x1)
		return BOOT_DEVICE_FEL;
	return BOOT_DEVICE_SPINOR;
}

void print_string(const char *s){
	while(*s != 0) {
		if (*s == '\n') {
			sys_uart_putc('\r');
		}
		sys_uart_putc(*s);
		s ++;
	}
}

void print_number(uint32_t num){
	char str[16];
	int  i, n;

	n = 0;
	while (num > 0) {
		str[n ++] = num % 10;
		num = num / 10;
	}

	if (n == 0) {
	sys_uart_putc('0');
	} else {
		for (i = n;i > 0;i --) {
			sys_uart_putc(str[i - 1] + 0x30);
		}
	}
	sys_uart_putc('\r');
	sys_uart_putc('\n');
}

void print_hex(uint32_t num){
	char str[16];
	int  i, n;

	sys_uart_putc('0');
	sys_uart_putc('x');

	n = 0;
	while (num > 0) {
		str[n ++] = num & 0xf;
		num = num >> 4;
	}

	if (n == 0) {
		sys_uart_putc('0');
	} else {
		for (i = n;i > 0;i --) {
			if (str[i - 1] > 9) {
				sys_uart_putc(str[i - 1] + ('A' - 0x0a));
			} else {
				sys_uart_putc(str[i - 1] + 0x30);
			}
		}
	}
	sys_uart_putc('\r');
	sys_uart_putc('\n');
}


void boot_nand_flash(void){
	void * mem;
	void (*jamp)(void);
	struct flash_head *h;
	mem  = (void*)0x80000000;
	h    = mem;
	jamp = mem;
	print_string("Boot to SPI Nand Flash mode...\n");
	sys_spinand_init();
	sdelay(99999);
	sys_spinand_read(SIZE_START, mem, 16);
	sys_spinand_exit();
	if (h->magic[1] == 0xaa55aa55) {
		logout("Copy flashs offset 0x10000 to RAM 0x8000000 size:%d\r\n",h->magic[3]);
		sys_spinand_init();
		sys_spinand_read(SIZE_START, mem, h->magic[3]);
		sys_spinand_exit();
		print_string("Copy Flash Ok!\n");
	} else {
		print_string("Magic[1] == ");
		print_hex(h->magic[1]);
		print_string("Magic error\n");
		print_string("please check rtthread header 3 unit32 as follows [0]0xaa55aa55, [1]0 ,[2]image_size");
		while (1);
	}
	print_string("goto 0x80000000 ...\n");
	jamp();
}

void sys_copyself(void){
	int d = get_boot_device();

	void * mem;
	void (*jamp)(void);
	struct flash_head *h;
	mem  = (void*)0x80000000;
	h    = mem;
	jamp = mem;

	if(d == BOOT_DEVICE_FEL){
		print_string("Back to FEL mode...\n");
		return_to_fel();
	}else if(d == BOOT_DEVICE_SPINOR){
		print_string("Boot to SPI Nor Flash mode...\n");
		sys_spi_flash_init();
		sdelay(99999);
		sys_spi_flash_read(SIZE_START, mem, 16);
		sys_spi_flash_exit();
		if (h->magic[1] == 0xaa55aa55) {
			logout("Copy flashs offset 0x10000 to RAM 0x8000000 size:%d\r\n",h->magic[3]);
			sys_spi_flash_init();
			sys_spi_flash_read(SIZE_START, mem, h->magic[3]);
			sys_spi_flash_exit();
			print_string("Copy Flash Ok!\n");
		} else {
			print_string("Magic[1] == ");
			print_hex(h->magic[1]);
			print_string("Magic error\n");
			print_string("please check rtthread header 3 unit32 as follows [0]0xaa55aa55, [1]0 ,[2]image_size");
			while (1);
		}
		print_string("goto 0x80000000 ...\n");
		jamp();
	}else if(d == BOOT_DEVICE_SPINAND){
		/*
		print_string("Boot to SPI Nand Flash mode...\n");
		sys_spinand_init();
		sdelay(99999);
		sys_spinand_read(SIZE_START, mem, 16);
		sys_spinand_exit();
		if (h->magic[1] == 0xaa55aa55) {
			logout("Copy flashs offset 0x10000 to RAM 0x8000000 size:%d\r\n",h->magic[3]);
			sys_spinand_init();
			sys_spinand_read(SIZE_START, mem, h->magic[3]);
			sys_spinand_exit();
			print_string("Copy Flash Ok!\n");
		} else {
			print_string("Magic[1] == ");
			print_hex(h->magic[1]);
			print_string("Magic error\n");
			print_string("please check rtthread header 3 unit32 as follows [0]0xaa55aa55, [1]0 ,[2]image_size");
			while (1);
		}
		print_string("goto 0x80000000 ...\n");
		jamp();
		*/
		boot_nand_flash();
	}else if(d == BOOT_DEVICE_SDCARD){
		sdio_init();
		if(cpfile_to_ram("RTTHREADBIN",0x80000000) == 0){
			print_string("cp file from SD card to ram ok! Run....\n");
			sdelay(99999);
			jamp();
		}else{
			print_string("cp file from SD card to ram failed!, Boot to other way !!\n");
			//while(1);
			//start_nand_flash();
			boot_nand_flash();
		}
	}
}
